#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
免费用户处理系统
实现空闲时间处理、临时存储管理、微信公众号通知等功能
"""

import time
import json
import logging
import os
import tempfile
import shutil
from datetime import datetime, timedelta
from typing import Dict, List, Tuple, Optional
from dataclasses import dataclass
from enum import Enum
import threading
import queue
import hashlib

# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class ProcessingPriority(Enum):
    """处理优先级"""
    PAID_USER = 1
    FREE_USER = 2
    BATCH_PROCESSING = 3

class TaskStatus(Enum):
    """任务状态"""
    PENDING = "pending"
    PROCESSING = "processing"
    COMPLETED = "completed"
    FAILED = "failed"
    CANCELLED = "cancelled"

@dataclass
class FreeUserTask:
    """免费用户任务"""
    task_id: str
    user_id: str
    user_openid: str
    image_data: bytes
    image_filename: str
    submitted_time: datetime
    priority: ProcessingPriority
    status: TaskStatus
    processing_start_time: Optional[datetime] = None
    processing_end_time: Optional[datetime] = None
    result_url: Optional[str] = None
    error_message: Optional[str] = None

@dataclass
class ServerStatus:
    """服务器状态"""
    instance_id: str
    cpu_utilization: float
    memory_utilization: float
    active_tasks: int
    queue_length: int
    is_idle: bool
    last_activity_time: datetime

class FreeUserProcessingSystem:
    """免费用户处理系统"""
    
    def __init__(self):
        self.config = self._load_config()
        self.free_task_queue = queue.PriorityQueue()
        self.paid_task_queue = queue.PriorityQueue()
        self.temp_storage = TempStorageManager()
        self.wechat_notifier = WeChatNotifier()
        self.server_monitor = ServerMonitor()
        self.processing_threads = []
        self.is_running = False
        
    def _load_config(self) -> Dict:
        """加载配置"""
        return {
            'processing': {
                'idle_threshold': 0.3,  # CPU使用率低于30%认为空闲
                'idle_duration': 60,    # 空闲60秒后开始处理免费任务
                'batch_size': 10,       # 批处理大小
                'max_processing_time': 300,  # 最大处理时间5分钟
            },
            'storage': {
                'temp_dir': '/tmp/photoenhance',
                'max_temp_size_mb': 1000,  # 最大临时存储1GB
                'cleanup_interval': 3600,  # 1小时清理一次
                'file_retention_hours': 24,  # 文件保留24小时
            },
            'notification': {
                'template_id': 'TEMPLATE_ID',
                'retry_attempts': 3,
                'retry_delay': 30,  # 30秒重试间隔
            },
            'user_communication': {
                'pre_submit_notice': '免费模式需要等待，处理时机具有不确定性',
                'wechat_follow_reminder': '建议关注我们的公众号以获取处理通知',
                'upgrade_suggestion': '如需快速处理，可升级为付费用户',
            }
        }
    
    def start_system(self):
        """启动系统"""
        self.is_running = True
        
        # 启动处理线程
        self._start_processing_threads()
        
        # 启动监控线程
        self._start_monitoring_threads()
        
        logger.info("免费用户处理系统已启动")
    
    def stop_system(self):
        """停止系统"""
        self.is_running = False
        
        # 等待所有线程结束
        for thread in self.processing_threads:
            thread.join()
        
        # 清理临时存储
        self.temp_storage.cleanup_all()
        
        logger.info("免费用户处理系统已停止")
    
    def submit_free_task(self, user_id: str, user_openid: str, 
                        image_data: bytes, image_filename: str) -> Tuple[bool, str, str]:
        """提交免费用户任务"""
        try:
            # 生成任务ID
            task_id = self._generate_task_id(user_id, image_data)
            
            # 创建任务
            task = FreeUserTask(
                task_id=task_id,
                user_id=user_id,
                user_openid=user_openid,
                image_data=image_data,
                image_filename=image_filename,
                submitted_time=datetime.now(),
                priority=ProcessingPriority.FREE_USER,
                status=TaskStatus.PENDING
            )
            
            # 保存到临时存储
            temp_path = self.temp_storage.save_temp_file(task)
            task.temp_path = temp_path
            
            # 添加到队列
            self.free_task_queue.put((task.priority.value, task))
            
            logger.info(f"免费用户任务已提交: {task_id}")
            
            return True, task_id, self.config['user_communication']['pre_submit_notice']
            
        except Exception as e:
            logger.error(f"提交免费任务失败: {e}")
            return False, "", str(e)
    
    def _start_processing_threads(self):
        """启动处理线程"""
        # 启动空闲处理线程
        idle_thread = threading.Thread(target=self._idle_processing_worker)
        idle_thread.daemon = True
        idle_thread.start()
        self.processing_threads.append(idle_thread)
        
        # 启动批处理线程
        batch_thread = threading.Thread(target=self._batch_processing_worker)
        batch_thread.daemon = True
        batch_thread.start()
        self.processing_threads.append(batch_thread)
    
    def _start_monitoring_threads(self):
        """启动监控线程"""
        # 启动服务器监控线程
        monitor_thread = threading.Thread(target=self._server_monitoring_worker)
        monitor_thread.daemon = True
        monitor_thread.start()
        self.processing_threads.append(monitor_thread)
        
        # 启动存储清理线程
        cleanup_thread = threading.Thread(target=self._storage_cleanup_worker)
        cleanup_thread.daemon = True
        cleanup_thread.start()
        self.processing_threads.append(cleanup_thread)
    
    def _idle_processing_worker(self):
        """空闲处理工作线程"""
        while self.is_running:
            try:
                # 检查服务器是否空闲
                if self._is_server_idle():
                    # 处理免费任务
                    self._process_free_tasks_when_idle()
                
                time.sleep(10)  # 每10秒检查一次
                
            except Exception as e:
                logger.error(f"空闲处理工作线程异常: {e}")
                time.sleep(30)
    
    def _batch_processing_worker(self):
        """批处理工作线程"""
        while self.is_running:
            try:
                # 检查是否有足够的免费任务进行批处理
                if self.free_task_queue.qsize() >= self.config['processing']['batch_size']:
                    self._process_batch_tasks()
                
                time.sleep(60)  # 每分钟检查一次
                
            except Exception as e:
                logger.error(f"批处理工作线程异常: {e}")
                time.sleep(60)
    
    def _server_monitoring_worker(self):
        """服务器监控工作线程"""
        while self.is_running:
            try:
                # 更新服务器状态
                self.server_monitor.update_server_status()
                
                time.sleep(30)  # 每30秒更新一次
                
            except Exception as e:
                logger.error(f"服务器监控工作线程异常: {e}")
                time.sleep(60)
    
    def _storage_cleanup_worker(self):
        """存储清理工作线程"""
        while self.is_running:
            try:
                # 清理过期文件
                self.temp_storage.cleanup_expired_files()
                
                time.sleep(self.config['storage']['cleanup_interval'])
                
            except Exception as e:
                logger.error(f"存储清理工作线程异常: {e}")
                time.sleep(3600)
    
    def _is_server_idle(self) -> bool:
        """检查服务器是否空闲"""
        server_status = self.server_monitor.get_current_status()
        
        if not server_status:
            return False
        
        # 检查CPU使用率
        if server_status.cpu_utilization > self.config['processing']['idle_threshold'] * 100:
            return False
        
        # 检查活跃任务数
        if server_status.active_tasks > 0:
            return False
        
        # 检查空闲持续时间
        idle_duration = (datetime.now() - server_status.last_activity_time).total_seconds()
        if idle_duration < self.config['processing']['idle_duration']:
            return False
        
        return True
    
    def _process_free_tasks_when_idle(self):
        """在空闲时处理免费任务"""
        processed_count = 0
        max_tasks = 5  # 空闲时最多处理5个任务
        
        while processed_count < max_tasks and not self.free_task_queue.empty():
            try:
                # 获取任务
                priority, task = self.free_task_queue.get_nowait()
                
                # 处理任务
                success = self._process_single_task(task)
                
                if success:
                    processed_count += 1
                    logger.info(f"空闲时处理免费任务成功: {task.task_id}")
                else:
                    # 处理失败，重新加入队列
                    self.free_task_queue.put((priority, task))
                    break
                    
            except queue.Empty:
                break
            except Exception as e:
                logger.error(f"处理免费任务异常: {e}")
                break
    
    def _process_batch_tasks(self):
        """批处理任务"""
        batch_tasks = []
        
        # 收集批处理任务
        for _ in range(self.config['processing']['batch_size']):
            try:
                priority, task = self.free_task_queue.get_nowait()
                batch_tasks.append(task)
            except queue.Empty:
                break
        
        if not batch_tasks:
            return
        
        logger.info(f"开始批处理 {len(batch_tasks)} 个免费任务")
        
        # 批量处理
        for task in batch_tasks:
            try:
                self._process_single_task(task)
            except Exception as e:
                logger.error(f"批处理任务异常: {task.task_id}, {e}")
                task.status = TaskStatus.FAILED
                task.error_message = str(e)
    
    def _process_single_task(self, task: FreeUserTask) -> bool:
        """处理单个任务"""
        try:
            # 更新任务状态
            task.status = TaskStatus.PROCESSING
            task.processing_start_time = datetime.now()
            
            # 从临时存储读取图片
            image_data = self.temp_storage.read_temp_file(task.temp_path)
            
            # 处理图片
            result = self._process_image(image_data, task.image_filename)
            
            if result['success']:
                # 处理成功
                task.status = TaskStatus.COMPLETED
                task.processing_end_time = datetime.now()
                task.result_url = result['result_url']
                
                # 发送微信通知
                self.wechat_notifier.send_completion_notification(task)
                
                # 清理临时文件
                self.temp_storage.delete_temp_file(task.temp_path)
                
                logger.info(f"任务处理成功: {task.task_id}")
                return True
            else:
                # 处理失败
                task.status = TaskStatus.FAILED
                task.error_message = result['error']
                task.processing_end_time = datetime.now()
                
                # 发送失败通知
                self.wechat_notifier.send_failure_notification(task)
                
                # 清理临时文件
                self.temp_storage.delete_temp_file(task.temp_path)
                
                logger.error(f"任务处理失败: {task.task_id}, {result['error']}")
                return False
                
        except Exception as e:
            logger.error(f"处理任务异常: {task.task_id}, {e}")
            task.status = TaskStatus.FAILED
            task.error_message = str(e)
            task.processing_end_time = datetime.now()
            
            # 发送失败通知
            self.wechat_notifier.send_failure_notification(task)
            
            return False
    
    def _process_image(self, image_data: bytes, filename: str) -> Dict:
        """处理图片"""
        try:
            # 这里应该调用实际的图片处理服务
            # 模拟处理过程
            processing_time = 30 + (hash(image_data) % 20)  # 30-50秒
            
            # 模拟处理结果
            result = {
                'success': True,
                'result_url': f"https://example.com/results/{hash(image_data)}.jpg",
                'processing_time': processing_time,
                'enhanced_image': image_data,  # 实际应该是处理后的图片
            }
            
            return result
            
        except Exception as e:
            return {
                'success': False,
                'error': str(e)
            }
    
    def _generate_task_id(self, user_id: str, image_data: bytes) -> str:
        """生成任务ID"""
        timestamp = int(time.time())
        hash_input = f"{user_id}_{timestamp}_{image_data[:100]}"
        task_hash = hashlib.md5(hash_input.encode()).hexdigest()[:8]
        return f"free_{timestamp}_{task_hash}"
    
    def get_queue_status(self) -> Dict:
        """获取队列状态"""
        return {
            'free_queue_length': self.free_task_queue.qsize(),
            'paid_queue_length': self.paid_task_queue.qsize(),
            'server_status': self.server_monitor.get_current_status(),
            'temp_storage_usage': self.temp_storage.get_usage_info(),
        }

class TempStorageManager:
    """临时存储管理器"""
    
    def __init__(self, temp_dir: str = '/tmp/photoenhance'):
        self.temp_dir = temp_dir
        self._ensure_temp_dir()
    
    def _ensure_temp_dir(self):
        """确保临时目录存在"""
        os.makedirs(self.temp_dir, exist_ok=True)
    
    def save_temp_file(self, task: FreeUserTask) -> str:
        """保存临时文件"""
        filename = f"{task.task_id}_{task.image_filename}"
        file_path = os.path.join(self.temp_dir, filename)
        
        with open(file_path, 'wb') as f:
            f.write(task.image_data)
        
        return file_path
    
    def read_temp_file(self, file_path: str) -> bytes:
        """读取临时文件"""
        with open(file_path, 'rb') as f:
            return f.read()
    
    def delete_temp_file(self, file_path: str):
        """删除临时文件"""
        try:
            if os.path.exists(file_path):
                os.remove(file_path)
        except Exception as e:
            logger.error(f"删除临时文件失败: {file_path}, {e}")
    
    def cleanup_expired_files(self):
        """清理过期文件"""
        current_time = datetime.now()
        expired_count = 0
        
        for filename in os.listdir(self.temp_dir):
            file_path = os.path.join(self.temp_dir, filename)
            
            if os.path.isfile(file_path):
                file_mtime = datetime.fromtimestamp(os.path.getmtime(file_path))
                age_hours = (current_time - file_mtime).total_seconds() / 3600
                
                if age_hours > 24:  # 超过24小时
                    try:
                        os.remove(file_path)
                        expired_count += 1
                    except Exception as e:
                        logger.error(f"清理过期文件失败: {file_path}, {e}")
        
        if expired_count > 0:
            logger.info(f"清理了 {expired_count} 个过期临时文件")
    
    def cleanup_all(self):
        """清理所有临时文件"""
        try:
            shutil.rmtree(self.temp_dir)
            self._ensure_temp_dir()
            logger.info("已清理所有临时文件")
        except Exception as e:
            logger.error(f"清理所有临时文件失败: {e}")
    
    def get_usage_info(self) -> Dict:
        """获取使用情况信息"""
        total_size = 0
        file_count = 0
        
        for filename in os.listdir(self.temp_dir):
            file_path = os.path.join(self.temp_dir, filename)
            
            if os.path.isfile(file_path):
                total_size += os.path.getsize(file_path)
                file_count += 1
        
        return {
            'total_size_mb': total_size / (1024 * 1024),
            'file_count': file_count,
            'temp_dir': self.temp_dir,
        }

class WeChatNotifier:
    """微信通知器"""
    
    def __init__(self):
        self.template_id = "TEMPLATE_ID"
        self.retry_attempts = 3
        self.retry_delay = 30
    
    def send_completion_notification(self, task: FreeUserTask):
        """发送完成通知"""
        template_data = {
            "touser": task.user_openid,
            "template_id": self.template_id,
            "data": {
                "first": {"value": "您的图片处理已完成"},
                "keyword1": {"value": f"任务ID: {task.task_id}"},
                "keyword2": {"value": f"处理时间: {task.processing_end_time.strftime('%Y-%m-%d %H:%M:%S')}"},
                "keyword3": {"value": "处理状态: 成功"},
                "remark": {
                    "value": f"点击查看处理结果: {task.result_url}\n感谢使用免费服务！"
                }
            }
        }
        
        self._send_template_message(template_data, task.task_id)
    
    def send_failure_notification(self, task: FreeUserTask):
        """发送失败通知"""
        template_data = {
            "touser": task.user_openid,
            "template_id": self.template_id,
            "data": {
                "first": {"value": "您的图片处理失败"},
                "keyword1": {"value": f"任务ID: {task.task_id}"},
                "keyword2": {"value": f"失败时间: {task.processing_end_time.strftime('%Y-%m-%d %H:%M:%S')}"},
                "keyword3": {"value": f"失败原因: {task.error_message}"},
                "remark": {
                    "value": "请稍后重试，或联系客服获取帮助。"
                }
            }
        }
        
        self._send_template_message(template_data, task.task_id)
    
    def _send_template_message(self, template_data: Dict, task_id: str):
        """发送模板消息"""
        for attempt in range(self.retry_attempts):
            try:
                # 这里应该调用微信API发送模板消息
                # 模拟发送过程
                logger.info(f"发送微信通知: {task_id}")
                
                # 模拟发送成功
                return True
                
            except Exception as e:
                logger.error(f"发送微信通知失败 (尝试 {attempt + 1}/{self.retry_attempts}): {e}")
                
                if attempt < self.retry_attempts - 1:
                    time.sleep(self.retry_delay)
        
        logger.error(f"发送微信通知最终失败: {task_id}")
        return False

class ServerMonitor:
    """服务器监控器"""
    
    def __init__(self):
        self.current_status = None
        self.status_history = []
    
    def update_server_status(self):
        """更新服务器状态"""
        # 这里应该从实际的服务器监控系统获取状态
        # 模拟状态更新
        self.current_status = ServerStatus(
            instance_id="hai-instance-001",
            cpu_utilization=25.0,  # 模拟CPU使用率
            memory_utilization=60.0,  # 模拟内存使用率
            active_tasks=0,  # 模拟活跃任务数
            queue_length=5,  # 模拟队列长度
            is_idle=True,  # 模拟空闲状态
            last_activity_time=datetime.now() - timedelta(minutes=2)
        )
        
        # 保存历史状态
        self.status_history.append(self.current_status)
        
        # 保持历史记录在合理范围内
        if len(self.status_history) > 1000:
            self.status_history = self.status_history[-500:]
    
    def get_current_status(self) -> Optional[ServerStatus]:
        """获取当前状态"""
        return self.current_status

# 使用示例
def main():
    """使用示例"""
    system = FreeUserProcessingSystem()
    
    try:
        # 启动系统
        system.start_system()
        
        # 模拟提交免费任务
        user_id = "test_user_001"
        user_openid = "test_openid_001"
        image_data = b"fake_image_data"
        image_filename = "test.jpg"
        
        success, task_id, notice = system.submit_free_task(
            user_id, user_openid, image_data, image_filename
        )
        
        if success:
            print(f"任务提交成功: {task_id}")
            print(f"用户通知: {notice}")
        else:
            print(f"任务提交失败: {notice}")
        
        # 运行一段时间
        time.sleep(60)
        
        # 获取队列状态
        status = system.get_queue_status()
        print(f"队列状态: {status}")
        
    finally:
        # 停止系统
        system.stop_system()

if __name__ == '__main__':
    main()
